HybridMail Technical - Text Scanning
Updated: 31-03-2026 09:51

Text Scanning

Many XML elements can have the optional attribute scan . This defines a region of the letter from which to scan the text. The region is defined as X,Y,W,H where X,Y are the coordinates of the Top-Left corner of the region, measured in mm from the Top-Left corner of the page, and W,H are the width and height of the region in mm.

Schema

<nodeName [scan="X,Y,W,H"] 
		[simple="true|false"]
		[page="number"]
		[rightOf="{ref}"] 
		[leftOf="{ref}"] 
		[extract|extractLast="{ref}" [includeRef="true|false"]] 
		[numericOnly="true|false"] 
		[requiredChars="length"]
		[requiredLastChars="length"] 
		[requiredMidChars="start,length"] 
		[date|dateTime="true|false|future" [dateFormat="format-string"] [mustHave="true|false"]]
		[joinLines="true|false"] 
		[splitLines="split-text"]
		[replace="find-text~replace-text"]
		[matching="regex"]>regular-expression</nodeName>

Scan modifier attributes

Any XML element supporting the scan attribute implicitly also supports the optional simple and page attributes, as well as all the optional post-processing attributes, as well as default literal text. N.B. If the literal text is enclosed in curly braces, then it specifies the name of an identifier. In this case the value of that identifier will be used as the default.

All attributes are optional

Both the scan attribute and the element literal text can be present together. If so, the literal text is used if the scan region is empty.

<nodeName>literaltext</nodeName>     <!-- use the literal text -->
<nodeName>{ident-name}</nodeName>    <!-- use the value of the identifier named 'ident-name' -->

<nodeName scan="20,89,70,8"/>    <!-- scan text from letter -->

<nodeName scan="20,89,70,8">literaltext</nodeName>    <!-- scan text from letter, but use literal text if scan region is empty -->
<nodeName scan="20,89,70,8">{my-ident}</nodeName>    <!-- scan text from letter, but use the value of the identifier named 'my-ident' if scan region is empty -->

<nodeName scan="X,Y,W,H" simple="true">

If true, the scanned text will be examines using a 'simple' instead of 'location' strategy. 'simple' retrieves the text in the order that it was added to the document; 'location' retrieves it in the order visible to the reader. The default is 'Location'

<nodeName scan="X,Y,W,H" page="number">

If present, the text will be scanned from the specified page. Use positive numbers to indicate the ‘nth’ page from the start of the letter and negative numbers for the ‘nth’ page from the end of the letter E.g. page="-2" means scan from the second-last page. N.B. The page must exist! The default is page 1.

Scan post-processing attributes

These optional attributes are applied after the text has been scanned. They also apply to the literal text if the scan region was empty.

All attributes are optional

Very Important!!

Regardless of the order in which they are written in the XML file, the attributes are processed in the order shown in the schema; and their effect is cummulative.

 

<nodeName rightOf="ref"/> and <nodeName leftOf="ref" />

If present, the scanned text will be examines for 'ref' and only the text to the right (or left) of the first occurrence of 'ref' will be returned. If 'ref' does not appear in the scanned text, an empty string is returned.

Scanned TextAttributeResult
Account: 12345WM00rightOf="123"45WM00
Account: 12345WM00leftOf="45"Account: 123
<nodeName extract="ref" /> and <nodeName extractLast="ref" />

If present, the scanned text is examined for 'ref' and only the text following 'ref' up to the end of the line will be returned. If 'ref' does not appear in the scanned text, an empty string is returned. extractLast is the same except that it searches for the last instance of 'ref'. If the includeRef="true" attribute is also present, then the 'ref' is also returned.

Scanned TextAttributeResult
Name: Fred
Account: 12345WM00
Name: George
Account: 12346WM01
extract="Account:"12345WM00
Name: Fred
Account: 12345WM00
Name: George
Account: 12346WM01
extractLast="Account:"12346WM01
Name: Fred
Account: 12345WM00
Name: George
Account: 12346WM01
extract="Account:" includeRef="true"Account: 12345WM00
<nodeName numericOnly="true|false" />

If present, the scanned text, stripped of all non-numeric characters, is returned.

Scanned TextAttributeResult
Account: 12345WM00numericOnly="true"1234500
<nodeName requiredChars="length" and requiredLastChars="length" />

If present, only the first (or last) "length" characters of the scanned text is returned. If " length" is larger than the length of the scanned text, then the entire scanned text is returned. If "length" is negative, then everything except the first or last "length" characters of the scanned text is returned.

Scanned TextAttributeResult
12345WM00requiredChars="3"123
12345WM00requiredChars="-4"5WM00
12345WM00requiredLastChars="3"M00
12345WM00requiredLastChars="-6"123
<nodeName requiredMidChars="start,length" />

If present, only "length" characters starting from "start" of the scanned text are returned. If "start" is larger than the length of the scanned text, then an empty string is returned. The returned string is never longer than the scanned text even if start + length exceeds the scanned text length.

Scanned TextAttributeResult
12345WM00requiredMidChars="3,4"45WM
<nodeName date="false|true|future" /> and <nodeName dateTime="false|true|future" />

If present, the scanned text is examined for a string that represents a valid date. If such a string is found, it is returned. If no valid date is found, then the entire scanned text is returned, unless the mustHave=true attribute is present, in which case an empty string is returned. If the attribute value is "true" then the first instance of a valid date is returned. If the attribute value is "future" then the first instance of a valid date in the future is returned.

dateTime is similar to date except that it searches for and returns the time as well as the date. If a valid date is found, but no valid time, then the date string is returned with the time component set to 00:00.

If the scanned text represents a valid date (and time), the additional attribute dateFormat="format-string"  can be added to format the returned text according to the format-string. The format string can contain new line character "\n" if desired.

Scanned TextAttributeResult
Following your last appointment on 14 Aug 2021,
your next appointment is on
the 21st September 2022 at 14:00
date="true"14 August 2021
Following your last appointment on 14 Aug 2021,
your next appointment is on
the 21st September 2022 at 14:00
date="future"21 September 2022
Following your last appointment on 14 Aug 2021,
your next appointment is on
the 21st September 2022 at 14:00
dateTime="true"14 August 2021 00:00
Following your last appointment on 14 Aug 2021,
your next appointment is on
the 21st September 2022 at 14:00
dateTime="future"21 September 2022 14:00
Your next appointment is on
the 21st September 2022 at 14:00
dateTime="future"
dateFormat="dddd dd MMMM yyyy at hh:mm"
Monday 21 September 2022 at 14:00
Your next appointment is on
the 21st September 2022 at 14:00
dateTime="future"
dateFormat="dd/MM/yy hh:mm"
21/09/22 14:00
N.B Searching for dates and times is a slower operation than normal text post-processing so, if used, it is best to combine this attribute with others so as to restrict the size of the text before examining it for a valid date.
<nodeName joinLines="true|false" />

If present, all lines in the scanned text are joined into one line, and then all multiple-whitespace is reduced to single spaces.

Scanned TextAttributeResult
Name: Fred
Name: George
Name: Harry
joinLines="true"Name: Fred Name: George Name: Harry
<nodeName splitLines="split-text" />

If present, a new line character "\n" is inserted before every instance of split-text.

Scanned TextAttributeResult
Name: Fred Name: George Name: HarrysplitLines="Name"Name: Fred
Name: George
Name: Harry
<nodeName replace="find-text~replace-text" />

If present, every instance of "find-text" is replaced by "replace-text". N.B. the tilde character ~ is mandatory and is used as a separator for the "find-text" and "replace-text". Use "\n" to represent a new line character in either the "find-text" or "replace-text" if desired.

Scanned TextAttributeResult
Name: Fred
Name: George
Name: Harry
replace="Name~Firstname"Firstname: Fred
Firstname: George
Firstname: Harry
<nodeName matching="regex" />

If present, the first instance of a substring in the scanned text that matches the regular expression is extracted.

Scanned TextAttributeResult
The postcode is TA1 1JJ and the number is 7matching="[A-Z][A-Z][1-9] [1-9][A-Z][A-Z]"TA1 1JJ